package com.deschen.security.core.validate.code.filter;

import com.deschen.security.core.properties.SecurityConstants;
import com.deschen.security.core.properties.SecurityProperties;
import com.deschen.security.core.validate.code.ValidateCodeProcessorHolder;
import com.deschen.security.core.validate.code.ValidateCodeType;
import com.deschen.security.core.validate.code.exception.ValidateCodeException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @Author deschen
 * @Create 2019/8/17
 * @Description 校验的验证码过滤器
 * @Since 1.0.0
 */
// OncePerRequestFilter 每次只会调用一次
//@Component
@Slf4j
@Component
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {

    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler;

    @Autowired
    private SecurityProperties securityProperties;

    @Autowired
    private ValidateCodeProcessorHolder validateCodeProcessorHolder;

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    // 存放所有需要校验验证码的url
    private Map<String, ValidateCodeType> urlMap = new HashMap<>();

    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();
        log.info("【初始化userMap】SecurityProperties的imageUrls = {}", securityProperties.getCode().getImage().getUrls() );
        urlMap.put(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM, ValidateCodeType.IMAGE);
        addUrlToMap(securityProperties.getCode().getImage().getUrls(), ValidateCodeType.IMAGE);

        log.info("【初始化userMap】SecurityProperties的smsUrls = {}", securityProperties.getCode().getSms().getUrls());
        urlMap.put(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE, ValidateCodeType.SMS);
        addUrlToMap(securityProperties.getCode().getSms().getUrls(), ValidateCodeType.SMS);
        log.info("【初始化userMap】urlMap = {}", urlMap);
    }

    private void addUrlToMap(String urls, ValidateCodeType type) {
        if (StringUtils.isNotBlank(urls)) {
            String[] urlArr = StringUtils.splitByWholeSeparatorPreserveAllTokens(urls, ",");
            for (String url : urlArr) {
                urlMap.put(url, type);
            }
        }
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        log.info("【图片验证过滤器】开始判断url，URI = {}",
                request.getRequestURI());
        ValidateCodeType validateCodeType = getValidateCodeType(request);
        if (null != validateCodeType) {
            log.info("【"+ validateCodeType + "验证过滤器】开始验证图片");
            try {
                validateCodeProcessorHolder.findValidateCodeProcessorByType(validateCodeType)
                        .validate(new ServletWebRequest(request, response),
                                validateCodeType.equals(validateCodeType.IMAGE) ?
                                        request.getParameter(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY) :
                                        request.getParameter(SecurityConstants.DEFAULT_PARAMETER_NAME_MOBILE));
            } catch (ValidateCodeException exception) {
                authenticationFailureHandler.onAuthenticationFailure(request, response, exception);
                return;
            }
        }
        filterChain.doFilter(request, response);

    }

    private ValidateCodeType getValidateCodeType(HttpServletRequest request) {
        ValidateCodeType validateCodeType = null;
        if (StringUtils.equalsAnyIgnoreCase(request.getMethod(), "post")) {
            Set<String> urls = urlMap.keySet();
            for (String url :
                    urls) {
                if (antPathMatcher.match(url, request.getRequestURI())) {
                    validateCodeType = urlMap.get(url);
                    break;
                }
            }
        }
        return validateCodeType;
    }


}
